package com.example.xbrother.rule;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.example.xbrother.listener.NoModelDataListener;
import com.example.xbrother.rule.excel.ExcelNoModelData;
import com.example.xbrother.rule.function.ExcelDataFilter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 基础bean
 */
@Slf4j
public class BaseBean {
    /**
     * 打印日志
     */
    public void log(Object msg) {
        log.info("{}", JSON.toJSONString(msg));
    }

    /**
     * 添加物料
     */
    public void add(BaseParam p, String code, Integer num) {
        List<MaterialResult> r = p.getResult();
        r.add(new MaterialResult(code, num));
    }

    /**
     * 添加物料
     */
    public void add(BaseParam p, String code, String name, Integer num) {
        List<MaterialResult> r = p.getResult();
        r.add(new MaterialResult(code, name, num));
    }

    /**
     * 查找第一个大于它的数组下标值
     *
     * @param arr  数组
     * @param data 对比值
     * @return 下标
     */
    private Integer firstBiggerDataIndex(double[] arr, double data) {
        Integer index = null;
        if (arr.length == 0) {
            return index;
        }

        for (int i = 0; i < arr.length; i++) {
            // 第一个比data大的数据
            if (data <= arr[i]) {
                index = i;
                break;
            }
        }
        return index;
    }

    /**
     * 最接近的数值
     *
     * @param srcData   原始值
     * @param data      查找值
     * @param stopTimes 循环停止倍数值
     * @return [下标, 倍数]
     */
    public int[] latestData(double[] srcData, double data, int stopTimes) {
        if (stopTimes < 1) {
            return null;
        }
        int[] result = new int[2];
        for (int i = 1; i <= stopTimes; i++) {
            final int times = i;
            double[] arrTemp = Arrays.stream(srcData).map(x -> x * times).toArray();
            Integer indexTemp = firstBiggerDataIndex(arrTemp, data);
            if (indexTemp != null) {
                result[0] = indexTemp;
                result[1] = times;
                break;
            }
        }
        return result;
    }

    /**
     * 函数式编程: 过滤数据
     */
    private static List<Map<Integer, String>> filterData(List<Map<Integer, String>> srcList, ExcelDataFilter predicate) {
        List<Map<Integer, String>> list = new ArrayList<>();
        srcList.stream().forEach(x -> {
            if (predicate.isDataOk(x)) {
                list.add(x);
            }
        });
        return list;
    }

    /**
     * 函数式编程: 过滤数据
     */
    private static List<Map<Integer, String>> filterData(List<Map<Integer, String>> srcList, Predicate<Map<Integer, String>> predicate) {
        List<Map<Integer, String>> list = new ArrayList<>();
        srcList.stream().forEach(x -> {
            if (predicate.test(x)) {
                list.add(x);
            }
        });
        return list;
    }

    /**
     * 读取excel中的数据
     *
     * @param excelFile excel文件
     * @param sheetNo   excel中的sheet下标，从0开始
     * @param xIndex    x轴的值在excel中的下标，即head所在行index
     *                  <p>
     *                  0 - This Sheet has no head ,since the first row are the data
     *                  <p>
     *                  1 - This Sheet has one row head , this is the default
     *                  <p>
     *                  2 - This Sheet has two row head ,since the third row is the data
     */
    public static ExcelNoModelData readExcelData(File excelFile, Integer sheetNo, Integer xIndex) {
        ExcelNoModelData excelNoModelData = new ExcelNoModelData();
        // 1. 读取excel中body数据
        NoModelDataListener noModelDataListener = new NoModelDataListener();
        List<Map<Integer, String>> listMap = EasyExcel.read(excelFile, noModelDataListener).headRowNumber(xIndex).sheet(sheetNo).doReadSync();
        for (Map<Integer, String> data : listMap) {
            // 返回每条数据的键值对 表示所在的列 和所在列的值
            // {0:"5",1:"2*12*2",2:"2*12*2",3:"2*24",4:"2*24*2",5:"2*24*2",6:"2*24*2"}
            log.info("读取到数据:{}", JSON.toJSONString(data));
        }
        excelNoModelData.setBodyData(listMap);
        // 2. 读取head
        Map<Integer, String> headMap = noModelDataListener.getCachedHeadMap();
        // head===>{0:"单柜功率\nIT柜数量",1:"3",2:"4",3:"5",4:"6",5:"7",6:"8"}
        log.info("head===>{}", JSON.toJSONString(headMap));
        excelNoModelData.setHead(headMap);
        return excelNoModelData;
    }

    /**
     * 解析Excel并快速定位到物料信息
     *
     * @param xValue          x轴的值
     * @param yValue          y轴的值
     * @param yIndex          y轴的值在excel中的下标，即column的index，从0开始
     * @param excelDataFilter excel数据过滤器
     */
    public static String positionMaterialInExcel(ExcelNoModelData excelNoModelData, Object xValue, Object yValue, Integer yIndex, ExcelDataFilter excelDataFilter) {
        List<Map<Integer, String>> listMap = excelNoModelData.getBodyData();
        // 1. 是否需要过滤数据
        if (excelDataFilter != null) {
            listMap = filterData(listMap, excelDataFilter);
        }

        // 2. listMap 做一个索引-> 使的纵坐标的值作为key
        Map<String, List<Map<Integer, String>>> yValueGroupMap = listMap.stream().collect(Collectors.groupingBy(x -> x.get(yIndex)));
        log.info("按y轴的值进行分组后= {}", JSON.toJSONString(yValueGroupMap));

        // 3. headMap 做一个索引-> 使横坐标值作为key
        Map<String, Integer> headIndexMap = new HashMap<>();
        excelNoModelData.getHead().forEach((k, v) -> {
            headIndexMap.put(v, k);
        });
        // 4. 当给 机柜数量和机柜功率 能很快的定位到物料 eg: 5,33 对应的数据为 2*24*3+24*3+24*1
        return parseMaterialInExcel(headIndexMap, yValueGroupMap, xValue, yValue);
    }

    /**
     * 解析Excel并快速定位到物料信息
     *
     * @param excelFile       excel文件
     * @param sheetNo         excel中的sheet下标，从0开始
     * @param xValue          x轴的值
     * @param yValue          y轴的值
     * @param xIndex          x轴的值在excel中的下标，即head所在行index
     *                        <p>
     *                        0 - This Sheet has no head ,since the first row are the data
     *                        <p>
     *                        1 - This Sheet has one row head , this is the default
     *                        <p>
     *                        2 - This Sheet has two row head ,since the third row is the data
     * @param yIndex          y轴的值在excel中的下标，即column的index，从0开始
     * @param excelDataFilter excel数据过滤器
     */
    public static String positionMaterialInExcel(File excelFile, Integer sheetNo, Object xValue, Object yValue, Integer xIndex, Integer yIndex, ExcelDataFilter excelDataFilter) {
        ExcelNoModelData excelNoModelData = readExcelData(excelFile, sheetNo, xIndex);
        return positionMaterialInExcel(excelNoModelData, xValue, yValue, yIndex, excelDataFilter);
    }

    /**
     * 解析Excel并快速定位到物料信息
     *
     * @param excelFile excel文件
     * @param sheetNo   excel中的sheet下标，从0开始
     * @param xValue    x轴的值
     * @param yValue    y轴的值
     * @param xIndex    x轴的值在excel中的下标，即head所在行index
     *                  <p>
     *                  0 - This Sheet has no head ,since the first row are the data
     *                  <p>
     *                  1 - This Sheet has one row head , this is the default
     *                  <p>
     *                  2 - This Sheet has two row head ,since the third row is the data
     * @param yIndex    y轴的值在excel中的下标，即column的index，从0开始
     */
    public static String positionMaterialInExcel(File excelFile, Integer sheetNo, Object xValue, Object yValue, Integer xIndex, Integer yIndex) {
        return positionMaterialInExcel(excelFile, sheetNo, xValue, yValue, xIndex, yIndex, null);
    }

    /**
     * 根据 x轴和y轴的值 快速定位到物料
     *
     * @param xMap   excel中的x轴数据 例如：{0:"单柜功率\nIT柜数量",1:"3",2:"4",3:"5",4:"6",5:"7",6:"8"}
     * @param yMap   excel中的y轴数据 例如: {"5":[{0:"5",1:"2*12*2",2:"2*12*2",3:"2*24",4:"2*24*2",5:"2*24*2",6:"2*24*2"}]}
     * @param xValue x轴的值 例如： 单个IT功率
     * @param yValue y轴的值 例如：IT机柜数
     */
    private static String parseMaterialInExcel(Map<String, Integer> xMap, Map<String, List<Map<Integer, String>>> yMap, Object xValue, Object yValue) {
        // 当给 机柜数量和机柜功率 能很快的定位到物料 eg: 5,33 对应的数据为 2*24*3+24*3+24*1
        Integer xIndex = xMap.get(String.valueOf(xValue));
        List<Map<Integer, String>> list = yMap.get(String.valueOf(yValue));
        Map<Integer, String> dataMap = list.get(0);
        String materialStr = dataMap.get(xIndex);
        log.info("xValue = {}  yValue = {} 对应的数据为 {}", xValue, yValue, materialStr);
        return materialStr;
    }

    /**
     * 合并物料结果：把相同物料合并&移除num为0的物料
     */
    public static void mergeMaterialResult(BaseParam p) {
        List<MaterialResult> result = p.getResult();
        log.info("merge前的数据 = {}", JSON.toJSONString(result));
        List<MaterialResult> afterMergeResult = new ArrayList<>();
        Map<String, List<MaterialResult>> groupByCodeMap = result.stream().collect(Collectors.groupingBy(x -> x.getCode()));
        for (String code : groupByCodeMap.keySet()) {
            List<MaterialResult> resultTemp = groupByCodeMap.get(code);
            MaterialResult materialResultTemp = resultTemp.stream()
                    .reduce(new MaterialResult(), (c, n) -> {
                        if (c != null) {
                            if (c.getNum() == null) {
                                c.setNum(0);
                            }
                            int numTemp = c.getNum() + n.getNum();
                            c.setNum(numTemp);
                        }
                        return c;
                    });
            if (materialResultTemp.getNum() == 0) {
                continue;
            }
            MaterialResult materialResult = resultTemp.get(0);
            materialResult.setNum(materialResultTemp.getNum());
            afterMergeResult.add(materialResult);
        }
        log.info("merge后的数据 = {}", JSON.toJSONString(afterMergeResult));
        p.setResult(afterMergeResult);
    }
}
